/* 
 * File:   script.h
 * Author: bjmey
 *
 * Created on September 5, 2007, 9:41 PM
 */

#ifndef _SCRIPT_H
#define	_SCRIPT_H
#include "rope.h"


extern "C" {
	
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
	
}

/* 
     We need Lua, of course. 
     I assume Lua has been compiled as a C library, and not as a C++ one,
      because you can do the latter (althoug default, it does the former. )
*/



typedef struct lua_State * Luastate;
typedef lua_CFunction Luafunction;

/*
extern Script 	script_make();
extern Script 	script_free( Script self);
extern Script 	script_eval_c( Script self, const char * script ); 
extern Script 	script_eval_r( Script self, Rope script ); 
extern Script 	script_run(Script self, const char *filename ); 
extern Rope 	script_get_rope(Script self, const char * globalname, const char *def);

extern Script 	script_setglob_string(Script self, const char *varname, const char *value );
*/

struct LuaValue; 

#include <map> 

typedef std::map < LuaValue , LuaValue > LuaTable;

class LuaValue
{

protected:
 enum {
	LUA_PP_NIL,
	LUA_PP_BOOLEAN,
	LUA_PP_NUMBER,
    LUA_PP_STRING,
    LUA_PP_CFUNCTION,
    LUA_PP_TABLE,
	LUA_PP_USERDATA
 };

 int _type;
 LuaTable _table; 	

 union {
  bool 			_boo;	
  double        _num;
  char*         _str;
  const char*   _cstr;
  Luafunction   _fnc;
  void*         _ptr;
  const void*   _cptr;
  int           _ref;	// used by derived class
 };

			// Protected constructor to create references
 LuaValue (int ref, int mark) : _type(mark), _ref(ref) {}
public:
 //* CREATORS
 // The following constructors are available,
 // allowing automatic convertion from 
 // C++ primitive types.

			//* Create a 'nil' object
 LuaValue (void)
					: _type(LUA_PP_NIL) {}
			//* Create a 'number' from an 'int'
 LuaValue(bool v) :  _type(LUA_PP_BOOLEAN), _boo(v) {}
 LuaValue (int v) 			
					: _type(LUA_PP_NUMBER),   _num(v)  {}
			//* Create a 'number' from a 'double'
 LuaValue (float v) 			
					: _type(LUA_PP_NUMBER),   _num(v)  {}
			//* Create a 'number' from a 'double'
 LuaValue (double v) 			
					: _type(LUA_PP_NUMBER),   _num(v)  {}
			//* Create a 'string' from a 'char*'
 LuaValue (char* v)		        
					: _type(LUA_PP_STRING),   _str(v)  {}
			//* Create a 'string' from a 'const char*'
 LuaValue (const char* v)		
					: _type(LUA_PP_STRING),   _cstr(v) {}
			//* Create a 'cfunction' from a 'lua_CFunction}
 LuaValue (lua_CFunction v)		
					: _type(LUA_PP_CFUNCTION),_fnc(v)  {}
			//* Create a 'userdata' from a 'void*'
 LuaValue (void* v, int tag=LUA_PP_USERDATA)	
					: _type(tag),            _ptr(v)  {}
			//* Create a 'userdata' from a 'const void*'
 LuaValue (const void* v, int tag=LUA_PP_USERDATA) 
					: _type(tag),            _cptr(v) {}
			//* Copy and assignment
 LuaValue (const LuaValue& v)  { *this = v; }
 LuaValue& operator= (const LuaValue& v) 
 {
  _type = v._type;
  _num = v._num;
  return *this;
 }
  

 // Destructor
 virtual ~LuaValue (void) {}

 // * Query if 'nil'
 virtual int is_nil      (void) const 
					{ return type()==LUA_PP_NIL; }
 			//* Query if 'number'
 virtual int is_number   (void) const 
					{ return type()==LUA_PP_NUMBER; }
 			//* Query if 'string'
 virtual int is_string   (void) const 
					{ return type()==LUA_PP_STRING; }
 			//* Query if 'table'
 virtual int is_table    (void) const 
					{ return 0; } // never happens
 			//* Query if 'function'
 virtual int is_function (void) const 
					{ return type()==LUA_PP_CFUNCTION; }
 			//* Query if 'userdata'
 virtual int is_data (void) const 
					{ return type()>=LUA_PP_USERDATA; }
 			//* Return the corresponding Lua type
 virtual int type(void) const 
					{ return _type; }

 //* CONVERSION
 // Allow conversion to C++ primitive types.

 //* Convert to 'float'
 virtual operator float() const { 
	return _type==LUA_PP_NUMBER ? _num : 0; 
 }
 //* Convert to {char*}
 virtual operator char*() const { 
	return _type==LUA_PP_STRING ? (char*)_str : 0; 
 }
 //* Convert to {const char*}
 virtual operator const char*() const { 
	return _type==LUA_PP_STRING ? _str : 0; 
 }
 //* Convert to {void*}
 virtual operator void*() const { 
	return _type>=LUA_PP_USERDATA ? (void*)_ptr : 0; 
 }
  //* Convert to {const void*}
 virtual operator const void*() const { 
	return _type>=LUA_PP_USERDATA ? _ptr : 0; 
 }
  //* Convert to {bool}
 virtual operator bool() const { 
	return _type>=LUA_PP_BOOLEAN ? _boo : false; 
 }



};



class Lua
{
  	public:
  /* Sets up pointer to lua state, and loads the Lua libs.*/
	Lua(void); 
	Lua(Luastate state);
	~Lua(void); 
	int Lua::eval_c(const char * script);
	int Lua::eval_r(Rope script );
	int Lua::run(const char *filename);
	
  

  enum { STACKTOP = -1, STACKBOTTOM = 1, GLOBALS = LUA_GLOBALSINDEX, REGISTRY = LUA_REGISTRYINDEX };
  
  void getglobal(const char *varname);
  void getglobalx(const char *varnamex, const char * assignto= "_TSUKILUA_RESULT");
  void setglobal(const char *varname);
  void setglobalx(const char *varnamex, const char * assignto= "_TSUKILUA_RESULT");
 
  void setdouble(const char *varname, double val)
  {  
    pushstring(varname);
    pushnumber(val);       
    rawset(LUA_GLOBALSINDEX);
  }
  
  void setint(const char *varname, long val)
  {  
    pushstring(varname);
    pushnumber(val);       
    rawset(LUA_GLOBALSINDEX);
  }
  
  void setbool(const char *varname, bool val)
  {  
    pushstring(varname);
    pushboolean(val);       
    rawset(LUA_GLOBALSINDEX);
  }
  
  void setstring(const char *varname,  const Rope & val)
  {  
    pushstring(varname);
    pushstring(val.c_str());       
    rawset(LUA_GLOBALSINDEX);
  }
  
  void setfunction(const char *varname, Luafunction f, int index = LUA_GLOBALSINDEX)  
  { 
     pushstring(varname);
     pushcfunction(f);  
     rawset(index);
  }

  void settablefunction(const char *tablename, const char *varname, lua_CFunction f)  
  { 
     tableintable(tablename, LUA_GLOBALSINDEX);
     pushstring(varname);
     pushcfunction(f);  
     rawset(-3);
     pop();     
  }
  
  void setregistrytablefunction(const char *tablename, const char *varname, lua_CFunction f)  
  { 
     tableintable(tablename, LUA_REGISTRYINDEX);
     pushstring(varname);
     pushcfunction(f);  
     rawset(-3);
     pop();     
  }
 

  double getdouble(const char *varname, double def)
  {  
    getglobalx(varname);
    if(!isnumber()) { pop(1); return def; }     
    return tonumber(); 
  }
  
  long getint(const char *varname, long def)
  {  
    getglobalx(varname);
    if(!isnumber()) { pop(1); return def; }     
    return  (int) tonumber(); 
  }
  
  bool getbool(const char *varname, bool def)
  {  
    getglobalx(varname);
    if(!isboolean()) { pop(1); return def; }     
    return  (bool)toboolean(); 
  }
  
  Rope getstring(const char *varname,  const Rope & def)
  { 
    Rope res;      
    getglobalx(varname);
    if(!isstring()) { pop(1); return def; }     
    res = tostring();
    res += ""; /* Force copy-on-write */
    pop(1); 
    return res; 
  }
  
  Rope get(const char *varname,  const Rope & def)  { return getstring(varname,def); }
  double get(const char *varname, double def)       { return getdouble(varname,def); }
  
  void argdiscard(int count = 0)  {  if(gettop() > count) remove(1);    }
  
  double argdouble(double def)  { 
    double res;        
    if(!isnumber(1)) { argdiscard(); return def; }     
    res = tonumber(1); 
    argdiscard();
    return res;
  }
  
  long argint(long def)  {  
    long res;      
    if(!isnumber(1)) { argdiscard(); return def; }     
    res = (long) tonumber(1); 
    argdiscard();
    return res;
  }
  
  bool argbool(bool def)  {  
    bool res;      
    if(!isboolean(1)) { argdiscard(); return def; }     
    res = (bool)toboolean(1); 
    return res;
  }
  
  Rope argstring(const Rope & def)  { 
    Rope res;          
    if(!isstring(1)) { argdiscard(1); return def; }     
    res = tostring(1);
    res += ""; /* Force copy-on-write */
    argdiscard();
    return res; 
  }
  
  
  int checkstack (int extra) { return lua_checkstack (_state, extra); }
  int gettop     (void)      { return lua_gettop     (_state);        }
  void settop    (int index)     { lua_settop    (_state, index); }
  void pushvalue (int index)     { lua_pushvalue (_state, index); }
  void remove    (int index)     { lua_remove    (_state, index); }
  void insert    (int index)     { lua_insert    (_state, index); }
  void replace   (int index)     { lua_replace   (_state, index); }
  void pop       (int index = 1) { lua_pop       (_state, index); }

  /* Low level stack interface wrappers. */

  int gettype     (int index) { return lua_type       (_state, index); }
  /* Exception to the naming scheme!!! */

  int isnil       (int index = STACKTOP) { return lua_isnil      (_state, index); }
  int isboolean   (int index = STACKTOP) { return lua_isboolean  (_state, index); }
  int isnumber    (int index = STACKTOP) { return lua_isnumber   (_state, index); }
  int isstring    (int index = STACKTOP) { return lua_isstring   (_state, index); }
  int istable     (int index = STACKTOP) { return lua_istable    (_state, index); }
  int isfunction  (int index = STACKTOP) { return lua_isfunction (_state, index); }
  int iscfunction (int index = STACKTOP) { return lua_iscfunction(_state, index); }
  int isuserdata  (int index = STACKTOP) { return lua_isuserdata (_state, index); }
  
  void seterror(const Rope & err) {_lasterror = err; }
  const Rope & getlasterror(void) {return _lasterror;}
  
  const char *gettypename(int type) { return lua_typename (_state,  type); }
  /* Exception to the naming scheme!!! */
  
  /* This concludes the data type checking and naming low level functions. */
  int          toboolean     (int index = STACKTOP) 
  { return lua_toboolean(_state, index);     }
  lua_Number   tonumber      (int index = STACKTOP) 
  { return lua_tonumber (_state, index);     }
  const char * tostring (int index = STACKTOP) 
  {  
     return lua_tostring(_state, index);
  }
  size_t       strlen        (int index = STACKTOP) 
  { return lua_strlen   (_state, index);     } 
  void *       touserdata    (int index = STACKTOP) 
  { return lua_touserdata(_state, index);    }
  lua_CFunction tocfunction  (int index = STACKTOP)
  { return lua_tocfunction (_state, index);  }
   /* Low level access to data on stack. */
  int equal         (int index1, int index2)
  { return lua_equal (_state, index1,  index2); }
  int lessthan      (int index1, int index2)
  { return lua_lessthan (_state, index1,  index2); }
  /* Low level comparison. */

  void pushboolean  (int b)
  { lua_pushboolean ( _state, b );    }
  void pushlstring  (const char * s, size_t len)
  { lua_pushlstring (_state, s, len); }
  void pushnumber   (lua_Number n) { lua_pushnumber  (_state, n); }
  void pushstring   (const char * s)
  { lua_pushstring  (_state, s);       }
  void pushnil      (void)
  { lua_pushnil     (_state);          }
  void pushcfunction     (lua_CFunction f)
  { lua_pushcfunction    (_state, f);  }
  void pushlightuserdata (void * p)
  { lua_pushlightuserdata (_state, p); }


  int  getgccount       (void) { return lua_getgccount     (_state); }
  /* Low level garbage collector control. XXX: to be expanded if needed. */
  
  void *newuserdata (size_t size) /* Creates new full user data block */
  { return lua_newuserdata (_state, size); }

  int getmetatable(int objindex = STACKTOP)
  { return lua_getmetatable (_state, objindex); }
  int setmetatable(int objindex = STACKTOP)
  { return lua_setmetatable (_state, objindex); }
  /* Low level metatable operations. */

  int loadfile(const char *filename)
  {
    return luaL_loadfile (_state, filename);
  }

  int loadbuffer( const char *buff, size_t size , const char *name = 0)
  {
    return luaL_loadbuffer (_state, buff, size, name);
  }

  int load (void *data, const char *chunkname)
  {
    return lua_load(_state, _reader, data, chunkname);
  }
  
  int dofile(const char *filename)
  {
    int aid, res;
    aid = loadfile(filename);
    if(aid) { seterror(tostring()) ; /*Get the error message. */
	    return aid; }
    res = docall();
    return res;
  }

  int dostring( const char *buff, const char *name = 0)
  {
    return luaL_dostring (_state, buff);
  }

  void newtable (void)      { lua_newtable (_state);         }
  void gettable (int index) { lua_gettable (_state,  index); }
  void rawget   (int index) { lua_rawget   (_state,  index); }
  void settable (int index) { lua_settable (_state,  index); }
  void rawset   (int index) { lua_rawset   (_state,  index); }
  int next      (int index) { return lua_next (_state,  index); }

  void rawgeti  (int index, int n) { lua_rawgeti(_state, index, n); }
  void rawseti  (int index, int n) { lua_rawseti(_state, index, n); }



  void call(int nargs, int nresults = LUA_MULTRET)
  { lua_call (_state, nargs, nresults); }
  /* Low level function call. May be unsafe.  */
  int pcall(int nargs, int nresults = LUA_MULTRET, int errfunc = 0)
  { return lua_pcall (_state, nargs, nresults, errfunc) ; }
  /* Protected function call. Use this one in stead of call() for safe caling.  */

  int docall(int nargs =0, int nresults =0)
  { int aid = pcall(nargs, nresults);
    if(aid)  { seterror(tostring()) ; /*Get the error message. */}
    return aid; 
  }
  /*
     Simplified protected call. Assumes that all variables on the stack,
     apart from one (the function) are arguments for that function.
     By default, assumes no return values.
  */

  void error(const char *message=0) { lua_error(_state); }
  /* Low level error-raising function. */
  void concat(int n) { lua_concat (_state, n); }
  /* Concatenates strings on the lua stack. */

  void fregister(const char *name, lua_CFunction func)
  { lua_register(_state, name, func) ; }

  /*
     Naming scheme exception. register is a keyword.
     You can register simple function names.
     More complex names, which include table indirection do NOT work.
  */

  void pushclosure (lua_CFunction fn, int n)
  { lua_pushcclosure (_state, fn, n); }

  /* Pushes a C closure. */

  lua_State *GetState(void)  { return _state; }

  int table2map   (Rope tablename, Ropemap & map   );
  int table2vector(Rope tablename, Ropelist & vec);
   
   
  /* These two functions take a globally defined Lua table variable
     name, and extract the table's first-level members into either 
     a map of strings or a vector of strings.  Numerical values will 
     be converted to strings as well. As a side effect, the table's numerical
     values will be turned into strings. However, this should not cause problems, 
     as string-to-number convcersion in Lua is transparent.  
     In the map, the only the table's string keys are used, whilst
     in the vector, only numerical Lua indexes table are used, and stored in 
     the order they have in the lua table, at the end of vec .    
   */
  
  bool pushpointer(void * ptr)
  {  
     void **aid;      
     aid = (void **) newuserdata(sizeof(void*)); 
     if(!aid) return false   ;  
     (*aid) = ptr;
     /* Store the pointer. */     
     return true;     
  }
  
  void *topointer(int index = STACKTOP)
  {  
     void **aid;     
     aid = (void **) touserdata(index);
     if(!aid) return 0;     
     return (*aid);
     /* Return the stored pointer. */          
  }
  
  /* tableintable  gets a table in a table with name name on stack index index,
         and creates a new table if it doesln't already exist. Returns 0 if on table 
        created, 1 if a table was created, and negative on error.         
  */
  int tableintable(const char *name, int index = LUA_REGISTRYINDEX)
  {
    pushstring(name); /* "name" */
    rawget(index);    /* name? */
    if(isnil(-1)) 
    {
        remove(-1);
        newtable();          /* {} */
        pushstring(name);  /* {}, name */
        pushvalue(-2);   /*{}, name, {}*/    
        settable(index);
        return 1;    /* Leave the new table on the stack top. */
    }
    return 0; /* fetched table is on the stack top.. */   
  }

   bool pushpointermetatable
   /* Pushes a full userdata pointer, and sets it's metatable
          to be the named one from either the registry or global namespace. */ 
   (void * ptr, const char * metatablename, int metaindex = LUA_REGISTRYINDEX)
   { 
      if(!pushpointer(ptr)) return false;
      tableintable(metatablename, metaindex);
      setmetatable(-2);   
      return true;     
   }
  
  void initialise(void);
  void baselibopen(void);
  void tablibopen(void);
  void strlibopen(void);
  void mathlibopen(void);        
  void iolibopen(void);
  void oslibopen(void);
    
  /* 
	 The functions above initialise the various Lua standard libraries. 
	 BEWARE! iolib and oslib are unsafe for use with elevated privilleges, as they 
	 allow access to the file system, and to the OS. Be sure to drop any
	 extra privilleges the program might hold before calling   void iolibopen(void) or 
	 void oslibopen(void)
  */

  
  
  protected:
  lua_State *_state;
  Rope _lasterror;
  lua_Chunkreader _reader;
  bool _owned;
  
};


typedef class Lua Script;


#define tsukilua_boxpointer(L,u) \
        (*(void **)(l.newuserdata(L, sizeof(void *))) = (u))

#define tsukilua_unboxpointer(L,i)   (*(void **)(lua_touserdata(L, i)))


/*
   Some macros to facilitate C++ integration.
 */


#define TSUKILUA_DECLARE(FUNCTIONNAME, CLASSNAME) \
	static int l_##FUNCTIONNAME(lua_State *) \
	{ return FUNCTIONNAME(); }

#define TSUKILUA_INSTALL(FUNCTIONNAME) \
	do { fregister( #FUNCTIONNAME , l_##FUNCTIONNAME) ;} while(0)







#endif	/* _SCRIPT_H */

